/*
 * Created on April 2, 2007
 * 
 * Copyright (c) Patrick Armstrong 2002-2007. All Rights Reserved 
 */
package org.dime.mine.bot;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.dime.mine.game.Board;
import org.dime.mine.game.actions.Mark;

/**
 * TestBoard // TODO Add type summary
 * 
 * @author Owner
 */
public class TestBoard extends AwareBoard
{
    public static final int NO_INFO = Integer.MAX_VALUE;

    private final boolean won, lost;

    private final Mark[] assumption;
    private final TestLocation[][] testBoard;
    private int marks = 0;

    /**
     * @param board
     */
    public TestBoard(Board board)
    {
        this(board, new LinkedList<Mark>());
    }

    /**
     * @param board
     */
    public TestBoard(Board board, List<Mark> assumption)
    {
        super(board);
        this.won = super.isWon();
        this.lost = super.isLost();

        this.assumption = assumption.toArray(new Mark[assumption.size()]);
        this.marks = board.getMarks();
        this.testBoard = new TestLocation[super.getRows()][super.getColumns()];

        for (int i = 0; i < super.getRows(); i++)
        {
            for (int j = 0; j < super.getColumns(); j++)
            {
                testBoard[i][j] = new TestLocation(board.getLocation(i, j));
            }
        }

    }

    public Mark[] getAssumption()
    {
        return assumption;
    }

    @Override
    public int check(int row, int column)
    {
        return testBoard[row][column].getValue();
    }

    @Override
    public boolean isGameOver()
    {
        return isWon() || isLost();
    }

    @Override
    public boolean isWon()
    {
        return won;
    }

    @Override
    public boolean isLost()
    {
        return lost;
    }

    @Override
    public int getMarks()
    {
        return marks;
    }

    @Override
    public void setMarked(int row, int column, boolean marked)
    {
        testBoard[row][column].setMarked(marked);
    }

    @Override
    public void open(int row, int column)
    {
        testBoard[row][column].open();
    }

    public void close(int row, int column)
    {
        testBoard[row][column].close();
    }

    @Override
    public Board.Location getLocation(int row, int column)
    {
        return getAwareLocation(row, column);
    }

    @Override
    public AwareBoard.AwareLocation getAwareLocation(int row, int column)
    {
        return testBoard[row][column];
    }

    @Override
    public String toString()
    {
        StringBuffer str = new StringBuffer();
        for (int i = 0; i < getRows(); i++)
        {
            for (int j = 0; j < getColumns(); j++)
            {
                str.append(testBoard[i][j].toString());
            }
            str.append("\n");
        }
        return str.toString();
    }

    @Override
    protected Set<AwareLocation> getOpenLocations()
    {
        Set<AwareLocation> set = super.getOpenLocations();
        Set<AwareLocation> set2 = new HashSet<AwareLocation>();
        for (AwareLocation loc : set)
        {
            if (loc.getValue() != NO_INFO)
                set2.add(loc);
        }
        return set2;
    }

    private class TestLocation extends AwareBoard.AwareLocation
    {
        private final int row;
        private final int column;
        private int value;

        public TestLocation(Board.Location loc)
        {
            super(loc.getRow(), loc.getColumn());

            this.row = loc.getRow();
            this.column = loc.getColumn();
            this.value = loc.getValue();
        }

        @Override
        public int getColumn()
        {
            return column;
        }

        @Override
        public int getRow()
        {
            return row;
        }

        @Override
        public int getValue()
        {
            return value;
        }

        @Override
        public int getKnowns()
        {
            int knowns = 0;

            for (AwareLocation loc : getNeighborhood())
            {
                if (loc.isOpen())
                    knowns++;
            }

            return knowns;
        }

        @Override
        public int getMarks()
        {
            int marks = 0;

            for (AwareLocation loc : getNeighborhood())
            {
                if (loc.isMarked())
                    marks++;
            }

            return marks;
        }

        @Override
        public int getUnknowns()
        {
            int unknowns = 0;

            for (AwareLocation loc : getNeighborhood())
            {
                if (!loc.isMarked() && !loc.isOpen())
                    unknowns++;
            }

            return unknowns;
        }

        @Override
        public boolean isMarked()
        {
            return value == Board.MARKED;
        }

        @Override
        public boolean isOpen()
        {
            return value != Board.MARKED && value != Board.NOT_OPEN;
        }

        @Override
        public boolean isUnknown()
        {
            return !(isOpen() || isMarked() || value == NO_INFO);
        }

        @Override
        public boolean isSatisfied()
        {
            return isOpen() && getValue() == getMarks();
        }

        @Override
        public void open()
        {
            if (!isOpen() && !isMarked())
            {
                value = NO_INFO;
                invalidateCalculations();
            }
        }

        public void close()
        {
            if (isOpen())
            {
                value = Board.NOT_OPEN;
                invalidateCalculations();
            }
        }

        @Override
        public void setMarked(boolean marked)
        {
            if (!isOpen())
            {
                if (marked && value != Board.MARKED)
                {
                    value = Board.MARKED;
                    marks++;
                    invalidateCalculations();
                }
                else if (!marked && value != Board.NOT_OPEN)
                {
                    value = Board.NOT_OPEN;
                    marks--;
                    invalidateCalculations();
                }
            }
        }

        @Override
        public boolean isValid()
        {
            if (isOpen() && getValue() != NO_INFO)
                return getUnknowns() >= (getValue() - getMarks())
                        && super.isValid();

            return true;
        }

        @Override
        public String toString()
        {
            int i = getValue();
            String c;
            switch (i)
            {
                case MARKED:
                    c = "*";
                    break;
                case NOT_OPEN:
                    c = "#";
                    break;
                case 0:
                    c = " ";
                    break;
                case NO_INFO:
                    c = ".";
                    break;
                default:
                    c = "" + i;
            }
            return c;
        }
    }
}
